package stream;

import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import java.util.OptionalInt;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import java.util.stream.Stream;

/**
 * @author zhangying
 * @date 2020-02-13
 * @description
 */
public class Dish {
    private final String name;
    private final boolean vegetarian;
    private final int calories;
    private final Type type;

    public Dish(String name, boolean vegetarian, int calories, Type type) {
        this.name = name;
        this.vegetarian = vegetarian;
        this.calories = calories;
        this.type = type;
    }

    public String getName() {
        return name;
    }

    public boolean isVegetarian() {
        return vegetarian;
    }

    public int getCalories() {
        return calories;
    }

    public Type getType() {
        return type;
    }

    public enum Type {MEAT, FISH, OTHER}

    public static void main(String[] args) {
        List<Dish> menu = Arrays.asList(
                new Dish("pork", false, 800, Dish.Type.MEAT),
                new Dish("beef", false, 700, Dish.Type.MEAT),
                new Dish("chicken", false, 400, Dish.Type.MEAT),
                new Dish("french fries", true, 530, Dish.Type.OTHER),
                new Dish("rice", true, 350, Dish.Type.OTHER),
                new Dish("season fruit", true, 120, Dish.Type.OTHER),
                new Dish("pizza", true, 550, Dish.Type.OTHER),
                new Dish("prawns", false, 300, Dish.Type.FISH),
                new Dish("salmon", false, 450, Dish.Type.FISH));

        //
        List<String> caloryOverThan300 = menu.stream()
                .filter(d -> d.getCalories() > 300)
                .map(Dish::getName)
                .limit(3)
                .collect(Collectors.toList());
        System.out.println(caloryOverThan300);

        System.out.println("------------------------");
        //循环合并
        menu.stream()
                .filter(d -> {
                    System.out.println("filter: " + d.getName());
                    return d.getCalories() > 300;
                })
                .map(d -> {
                    System.out.println("map: " + d.getName());
                    return d.getName();
                })
                .limit(3)
                .collect(Collectors.toList());
        System.out.println("=====================");
        //终端操作 forEach、collect、count
        menu.stream().forEach(dish -> System.out.println(dish.getName()));
        menu.stream().forEach(System.out::println);
        menu.forEach(System.out::println);
        long count = menu.stream().count();
        System.out.println(count);

        //筛选 filter
        List<String> collect = menu.stream()
                .filter(Dish::isVegetarian)
                .map(Dish::getName)
                .collect(Collectors.toList());
        System.out.println("筛选-filter：" + collect);

        //筛选各异的元素  distinct
        List<Integer> numbers = Arrays.asList(1, 2, 1, 3, 3, 2, 4);
        List<Integer> number = numbers.stream()
                .filter(i -> i % 2 == 0)
                .distinct()
                .collect(Collectors.toList());
        System.out.println("筛选各异-filter-distinct：" + number);

        //截断 limit
        List<String> limit = menu.stream()
                .filter(dish -> dish.getCalories() > 300)
                .map(Dish::getName)
                .limit(2)
                .collect(Collectors.toList());
        System.out.println("截断-limit：" + limit);

        //跳过 skip
        List<String> skip = menu.stream()
                .filter(dish -> dish.getCalories() > 300)
                .map(Dish::getName)
                .skip(2)
//                .limit(3)
                .collect(Collectors.toList());
        System.out.println("跳过-skip：" + skip);

        //映射 map
        List<String> map = menu.stream()
                .filter(dish -> dish.getCalories() > 300)
                .map(Dish::getName)
                .collect(Collectors.toList());
        System.out.println(map);

        //扁平化 flatMap
        String[] arrayOfWords = {"Goodbye", "World"};
        List<String> collect1 = Arrays.stream(arrayOfWords)
                .map(word -> word.split(""))
                .flatMap(Arrays::stream)
                .distinct()
                .collect(Collectors.toList());
        System.out.println(collect1);

        //给定两个数字列表，如何返回所有的数对呢？例如，给定列表[1, 2, 3]和列表[3, 4]，应
        //该返回[(1, 3), (1, 4), (2, 3), (2, 4), (3, 3), (3, 4)]。为简单起见，你可以用有两个元素的数组来代
        //表数对
        List<Integer> n1 = Arrays.asList(1, 2, 3);
        List<Integer> n2 = Arrays.asList(3, 4);
        for (int i = 0; i < n1.size(); i++) {
            for (int j = 0; j < n2.size(); j++) {

            }
        }
        /***********************************************************************/
        //检查谓词是否至少匹配一个元素 anyMatch
        boolean anyMatch = menu.stream()
                .anyMatch(Dish::isVegetarian);
        System.out.println("anyMatch: " + anyMatch);

        //检查谓词是否匹配所有元素 allMatch  nonMatch
        boolean al = menu.stream()
                .allMatch(Dish::isVegetarian);
        System.out.println("allMatch：" + al);

        boolean no = menu.stream()
                .noneMatch(Dish::isVegetarian);
        System.out.println("nonMatch：" + no);

        //查找 findAny
//        Optional<Dish> findAny = menu.stream()
//                .filter(Dish::isVegetarian)
//                .findAny();

        menu.stream()
                .filter(Dish::isVegetarian)
                .findAny()
                .ifPresent(dish -> System.out.println("findAny：" + dish.getName()));

        //查找第一个元素 findFirst
        Optional<Dish> findFirst = menu.stream()
                .findFirst();
        if (findFirst.isPresent()) {
            System.out.println("findFirst：" + findFirst.get().getName());
        }

        //归约
        //元素求和
        //List<Integer> numbers = Arrays.asList(1, 2, 1, 3, 3, 2, 4);
//        Optional<Integer> reduce = numbers.stream()
//                .reduce((a, b) -> a + b);
//        if (reduce.isPresent()) {
//            System.out.println("reduce 求和："+reduce.get());
//        }
        //和上面不一样 上面返回Optional  下面返回Integer
        Integer reduce = numbers.stream()
                .reduce(0, (a, b) -> a + b);
        System.out.println("reduce 求和：" + reduce);
        //最大值和最小值
        Optional<Integer> reduceMax = numbers.stream()
                .reduce(Math::max);
        if (reduceMax.isPresent()) {
            System.out.println("reduce max：" + reduceMax.get());
        }
        Optional<Integer> reduceMin = numbers.stream()
                .reduce(Math::min);
        if (reduceMin.isPresent()) {
            System.out.println("reduce min：" + reduceMin.get());
        }
        //数一数流中有多少个菜 map  reduce
        Integer dishCount = menu.stream()
                .map(dish -> 1)
                .reduce(0, (a, b) -> a + b);
//        long count1 = menu.stream().count();等同于上面
        System.out.println("有多少个菜：" + dishCount);

        //IntStream、 DoubleStream和LongStream，分别将流中的元素特化为int、 long和double，从而避免了暗含的装箱成本
        //映射到数值流
        int sum = menu.stream()
                .mapToInt(Dish::getCalories)
                .sum();//IntStream还支持其他的方便方法，如  max、 min、 average等
        System.out.println("intStream sum："+sum);
        //转换为对象流
        IntStream intStream = menu.stream().mapToInt(Dish::getCalories);
        Stream<Integer> boxed = intStream.boxed();
        //默认值OptionalInt  默认值0  和最大值为0 的区别
        OptionalInt max = menu.stream().mapToInt(Dish::getCalories).max();
        max.orElse(1);//如果没有最大值的话，显式提供一个默认最大值

        //数值范围
        System.out.println("range：");
        IntStream range = IntStream.range(1, 10);//不包含下界
        range.forEach(i-> System.out.print(i+","));
        System.out.println();
        System.out.println("rangeClosed：");
        IntStream rangeClosed = IntStream.rangeClosed(1, 10);
        rangeClosed.forEach(i-> System.out.print(i+","));
        System.out.println();

        //todo 勾股数    下次做

        //构建流
        //由值创建流
        Stream<String> stream = Stream.of("Java 8 ", "Lambdas ", "In ", "Action");
        stream.forEach(System.out::println);
        Stream<String> emptyStream = Stream.empty();//创建空的流
        //由数组创建流
        int[] ns = {2, 3, 5, 7, 11, 13};
        int nsSum = Arrays.stream(ns).sum();
        //由文件生成流

        //由函数生成流：创建无限流,需要用limit(n)做限制
        //1. 迭代
        Stream.iterate(0, n -> n + 2)
                .limit(10)
                .forEach(System.out::println);
        //2. 生成
        Stream.generate(Math::random).limit(5).forEach(System.out::println);
    }
}
